Code
# Clean workspace and load dependencies
rm(list = ls())
library(tidyr)
library(dplyr)
library(plotly)
library(ggplot2)
library(knitr)
library(lubridate)This is the code used to filter and manage the output of the Xsense dot IMU data. The source code can be found on my github
Lets start by defining a function to correctly load measurements:
Note that IMU’s do not start and stop measureing at the exact same time; even after synchronization the amount of elements per IMU (the length of measurement) differs. In my implementation of temporal relaignment I assumed that the time in SampletimeFine was synchronized, and excluded first or last elements accordingly to ensure dataframes are of equal length. This eases calculation since R prefers to calculate over lists of equal length.
# Clean workspace and load dependencies
rm(list = ls())
library(tidyr)
library(dplyr)
library(plotly)
library(ggplot2)
library(knitr)
library(lubridate)LoadXsenseData <- function(nameofpp) {
dir <- toString(nameofpp[2])
hz <- as.numeric(nameofpp[3])
skiprow <- as.numeric(nameofpp[4])
#dir <- bart
#hz <- 60
files <- list.files(path = dir, full.names = TRUE)
data <- list()
# Read CSV files of each directory
for (i in seq_along(files)) {
data[[i]] <- read.csv(files[i], header = TRUE, skip = skiprow)
}
# Ensure all dataframes have the same number of rows
min_rows <- min(sapply(data, nrow))
data <- lapply(data, function(df) {
df <- df[1:min_rows, , drop = FALSE]
return(df)
})
# Adjust time
for (i in seq_along(data)) {
rows <- nrow(data[[i]])
data[[i]]$TimeS <- ((1/hz) * (1:rows))
}
# Initialize toreturn data frame with time column
toreturn <- data.frame(time = data[[1]]$TimeS)
# Calculate absolute values
for (i in 1:length(data)) {
if ("FreeAcc_X" %in% names(data[[i]])) {
col_name <- paste0("FreeAcc_abs", i)
toreturn[[col_name]] <- sqrt(data[[i]]$FreeAcc_X^2 + data[[i]]$FreeAcc_Y^2 + data[[i]]$FreeAcc_Z^2)
}
if ("Acc_X" %in% names(data[[i]])) {
col_name <- paste0("A_abs", i)
toreturn[[col_name]] <- sqrt(data[[i]]$Acc_X^2 + data[[i]]$Acc_Y^2 + data[[i]]$Acc_Z^2)
}
if ("Gyr_X" %in% names(data[[i]])) {
col_name <- paste0("Gyr_abs", i)
toreturn[[col_name]] <- sqrt(data[[i]]$Gyr_X^2 + data[[i]]$Gyr_Y^2 + data[[i]]$Gyr_Z^2)
}
}
# Order the attributes of the dataframe
toreturn_sorted <- toreturn[, sort(names(toreturn))]
return(toreturn_sorted)
}#! Here i defined what test subject refers to what directorty
#! should probably think about PID here later on.
#! format: name, hz, skipheading
pp_info <- data.frame (
# name, file, hz, skiprow
bart = c("bart", "../../Logs/old/20240429_163145_bart/", 60, 7),
other = c("bart", "../../Logs/new/20240502_192335/", 60, 10)
)LoadXsenseData2 <- function(dir) {
hz <- 60
skiprow <- 7
files <- list.files(path = dir, full.names = TRUE)
data <- list()
# Read CSV files of each directory
for (i in seq_along(files)) {
data[[i]] <- read.csv(files[i], header = TRUE, skip = skiprow)
}
# Ensure all dataframes have the same number of rows
min_rows <- min(sapply(data, nrow))
data <- lapply(data, function(df) {
df <- df[1:min_rows, , drop = FALSE]
return(df)
})
# Adjust time
for (i in seq_along(data)) {
rows <- nrow(data[[i]])
data[[i]]$TimeS <- ((1/hz) * (1:rows))
}
# Initialize toreturn data frame with time column
toreturn <- data.frame(time = data[[1]]$TimeS)
# Calculate absolute values
for (i in 1:length(data)) {
if ("FreeAcc_X" %in% names(data[[i]])) {
col_name <- paste0("FreeAcc_abs", i)
toreturn[[col_name]] <- sqrt(data[[i]]$FreeAcc_X^2 + data[[i]]$FreeAcc_Y^2 + data[[i]]$FreeAcc_Z^2)
}
if ("Acc_X" %in% names(data[[i]])) {
col_name <- paste0("A_abs", i)
toreturn[[col_name]] <- sqrt(data[[i]]$Acc_X^2 + data[[i]]$Acc_Y^2 + data[[i]]$Acc_Z^2)
}
if ("Gyr_X" %in% names(data[[i]])) {
col_name <- paste0("Gyr_abs", i)
toreturn[[col_name]] <- sqrt(data[[i]]$Gyr_X^2 + data[[i]]$Gyr_Y^2 + data[[i]]$Gyr_Z^2)
}
}
# Order the attributes of the dataframe
toreturn_sorted <- toreturn[, sort(names(toreturn))]
return(toreturn_sorted)
}lets also define some functions to visualize the data
# Some functions to visualize the acceleration and the Gyr
plot_a <- function(df) {
if ("A_abs1" %in% names(df)) {
plot <- plot_ly(df, x = ~time, y = ~A_abs1, name = "marker1", type = "scatter", mode = "lines") %>%
add_trace(y = ~A_abs2, name = "marker 2") %>%
add_trace(y = ~A_abs3, name = "marker 3") %>%
add_trace(y = ~A_abs4, name = "marker 4") %>%
add_trace(y = ~A_abs5, name = "marker 5") %>%
layout(title = "Absolute accelerations",
xaxis = list(title = "Time"),
yaxis = list(title = "A_abs Values")) |>
bslib::card(full_screen = TRUE)
}
if ("FreeAcc_abs1" %in% names(df)) {
plot <- plot_ly(df, x = ~time, y = ~FreeAcc_abs1, name = "marker1", type = "scatter", mode = "lines") %>%
add_trace(y = ~FreeAcc_abs2, name = "marker 2") %>%
add_trace(y = ~FreeAcc_abs3, name = "marker 3") %>%
add_trace(y = ~FreeAcc_abs4, name = "marker 4") %>%
add_trace(y = ~FreeAcc_abs5, name = "marker 5") %>%
layout(title = "Absolute accelerations",
xaxis = list(title = "Time"),
yaxis = list(title = "FreeAcc_abs Values")) |>
bslib::card(full_screen = TRUE)
}
return(plot)
}
plot_gyr <- function(df) {
plot <- plot_ly(df, x = ~time, y = ~Gyr_abs1, name = "marker1", type = "scatter", mode = "lines") %>%
add_trace(y = ~Gyr_abs2, name = "marker 2") %>%
add_trace(y = ~Gyr_abs3, name = "marker 3") %>%
add_trace(y = ~Gyr_abs4, name = "marker 4") %>%
add_trace(y = ~Gyr_abs5, name = "marker 5") %>%
layout(title = "Absolute Gyr",
xaxis = list(title = "Time"),
yaxis = list(title = "Gyr Values"))
return(plot)
}
#! Maybe include a plotting function that takes the dataframe and the attribute to plot, assuming 5 markers?Lets load some data and see how it looks:
note: to increase performance I stored the calculated values and read them. This is faster than calculating all absolute values each time the program runs
# Storing the calculated data
# meting1 <- LoadXsenseData(pp_info[1:3,1])
# write.csv(meting1, file = "example1.csv", row.names = FALSE)
# Loading the calulated data
meting1 <- read.csv("example1.csv")
# Plot the calculated data
plot_a(meting1)